package com.iwjw.ibatis.criteria;

import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

import static com.iwjw.ibatis.criteria.Condition.IS_NULL;

/**
 * @author weiwei.huang
 * @Time 2016/10/12
 */
public class Query {

    protected String orderByClause;

    protected boolean distinct;

    protected List<Criteria> oredCriteria;

    public Query() {
        oredCriteria = new ArrayList<Criteria>();
    }

    public String getOrderByClause() {
        return orderByClause;
    }

    public void setOrderByClause(String orderByClause) {
        this.orderByClause = orderByClause;
    }

    public boolean isDistinct() {
        return distinct;
    }

    public void setDistinct(boolean distinct) {
        this.distinct = distinct;
    }

    public List<Criteria> getOredCriteria() {
        return oredCriteria;
    }

    public void or(Criteria criteria) {
        oredCriteria.add(criteria);
    }

    public Criteria or() {
        Criteria criteria = createCriteriaInternal();
        oredCriteria.add(criteria);
        return criteria;
    }

    public Criteria createCriteria() {
        Criteria criteria = createCriteriaInternal();
        if (oredCriteria.size() == 0) {
            oredCriteria.add(criteria);
        }
        return criteria;
    }

    protected Criteria createCriteriaInternal() {
        Criteria criteria = new Criteria();
        return criteria;
    }

    public void clear() {
        oredCriteria.clear();
        orderByClause = null;
        distinct = false;
    }


    protected abstract static class GeneratedCriteria {
        protected List<Criterion> criteria;

        protected GeneratedCriteria() {
            super();
            criteria = new ArrayList<Criterion>();
        }

        public boolean isValid() {
            return criteria.size() > 0;
        }

        public List<Criterion> getAllCriteria() {
            return criteria;
        }

        public List<Criterion> getCriteria() {
            return criteria;
        }

        protected void addCriterion(String sql) {
            if (StringUtils.isEmpty(sql)) {
                throw new RuntimeException("Sql cannot be null");
            }
            criteria.add(new Criterion(sql));
        }

        protected void addCriterion(Condition condition, String column) {
            if (condition == null || StringUtils.isEmpty(column)) {
                throw new RuntimeException("Column for condition cannot be null");
            }
            criteria.add(new Criterion(condition, column));
        }

        protected void addCriterion(Condition condition, Object value, String typeHandler, String column) {
            if (value == null || condition == null || StringUtils.isEmpty(column)) {
                throw new RuntimeException("Value for condition cannot be null");
            }
            criteria.add(new Criterion(condition, value, typeHandler, column));
        }

        protected void addCriterion(Condition condition, Object value1, Object value2, String typeHandler, String column) {
            if (value1 == null || value2 == null) {
                throw new RuntimeException("Between values for " + column + " cannot be null");
            }
            criteria.add(new Criterion(condition, value1, value2, typeHandler, column));
        }

        public Criteria andColumnSql(String sql) {
            addCriterion(sql);
            return (Criteria) this;
        }

        public Criteria andColumnIsNull(String column) {
            addCriterion(IS_NULL, column);
            return (Criteria) this;
        }

        public Criteria andColumnIsNotNull(String column) {
            addCriterion(Condition.IS_NOT_NULL, column);
            return (Criteria) this;
        }

        public Criteria andColumnEqualTo(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.EQUAL, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnNotEqualTo(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.NOT_EQUAL, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnGreaterThan(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.GREATER_THAN, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnGreaterThanOrEqualTo(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.GREATER_THAN_OR_EQUAL, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnLessThan(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.LESS_THAN, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnLessThanOrEqualTo(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.LESS_THAN_OR_EQUAL, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnIn(String column, List<?> values) {
            if (null != values && values.size() > 0) {
                addCriterion(Condition.IN, values, values.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnNotIn(String column, List<Object> values) {
            if (null != values && values.size() > 0) {
                addCriterion(Condition.NOT_IN, values, values.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnLike(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.LIKE, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnNotLike(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.NOT_LIKE, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria andColumnBetween(String column, Object value1, Object value2) {
            addCriterion(Condition.BETWEEN, value1, value2, value1.getClass().getName(), column);
            return (Criteria) this;
        }

        public Criteria andColumnNotBetween(String column, Object value1, Object value2) {
            addCriterion(Condition.NOT_BETWEEN, value1, value2, value1.getClass().getName(), column);
            return (Criteria) this;
        }


        public Criteria sql(String sql) {
            addCriterion(sql);
            return (Criteria) this;
        }

        public Criteria isNuLL(String column) {
            addCriterion(IS_NULL, column);
            return (Criteria) this;
        }

        public Criteria isNotNull(String column) {
            addCriterion(Condition.IS_NOT_NULL, column);
            return (Criteria) this;
        }

        public Criteria eq(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.EQUAL, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria notEq(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.NOT_EQUAL, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        /**
         * 大于
         *
         * @param column
         * @param value
         * @return
         */
        public Criteria greater(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.GREATER_THAN, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        /**
         * 大于等于
         *
         * @param column
         * @param value
         * @return
         */
        public Criteria greaterThan(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.GREATER_THAN_OR_EQUAL, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        /**
         * 小于
         *
         * @param column
         * @param value
         * @return
         */
        public Criteria less(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.LESS_THAN, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        /**
         * 小于等于
         *
         * @param column
         * @param value
         * @return
         */
        public Criteria lessThan(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.LESS_THAN_OR_EQUAL, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        public Criteria in(String column, List<?> values) {
            if (null != values && values.size() > 0) {
                addCriterion(Condition.IN, values, values.getClass().getName(), column);
            }
            return (Criteria) this;
        }


        public Criteria notIn(String column, List<Object> values) {
            if (null != values && values.size() > 0) {
                addCriterion(Condition.NOT_IN, values, values.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        /**
         * 模糊查询（匹配）
         *
         * @param column
         * @param value
         * @return
         */
        public Criteria like(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.LIKE, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        /**
         * 不匹配
         *
         * @param column
         * @param value
         * @return
         */
        public Criteria notLike(String column, Object value) {
            if (null != value) {
                addCriterion(Condition.NOT_LIKE, value, value.getClass().getName(), column);
            }
            return (Criteria) this;
        }

        /**
         * 包含
         *
         * @param column
         * @param value1
         * @param value2
         * @return
         */
        public Criteria between(String column, Object value1, Object value2) {
            addCriterion(Condition.BETWEEN, value1, value2, value1.getClass().getName(), column);
            return (Criteria) this;
        }

        /**
         * 不包含
         *
         * @param column
         * @param value1
         * @param value2
         * @return
         */
        public Criteria notBetween(String column, Object value1, Object value2) {
            addCriterion(Condition.NOT_BETWEEN, value1, value2, value1.getClass().getName(), column);
            return (Criteria) this;
        }

    }

    public static class Criteria extends GeneratedCriteria {

        protected Criteria() {
            super();
        }
    }


    public static class Criterion {
        @Getter
        private String column;

        private Condition condition;
        @Getter
        private Object value;
        @Getter
        private Object secondValue;
        @Getter
        private boolean noValue;
        @Getter
        private boolean singleValue;
        @Getter
        private boolean betweenValue;
        @Getter
        private boolean listValue;
        @Getter
        private String typeHandler;

        public Criterion(String sql) {
            this(null, null, null, sql);
        }

        public Criterion(Condition condition, Object value, String typeHandler, String column) {
            super();
            this.condition = condition;
            this.value = value;
            this.typeHandler = typeHandler;
            this.column = column;
            if (value instanceof List<?>) {
                this.listValue = true;
            } else {
                this.singleValue = true;
            }
            if (value == null) {
                this.noValue = true;
            }
        }


        public Criterion(Condition condition, String column) {
            this(condition, null, null, column);
        }

        public Criterion(Condition condition, Object value, String column) {
            this(condition, value, null, column);
        }

        public Criterion(Condition condition, Object value, Object secondValue, String typeHandler, String column) {
            super();
            this.condition = condition;
            this.value = value;
            this.secondValue = secondValue;
            this.typeHandler = typeHandler;
            this.betweenValue = true;
            this.column = column;
        }

        public Criterion(Condition condition, Object value, Object secondValue, String column) {
            this(condition, value, secondValue, null, column);
        }

        public String getCondition() {

            String conditon = "";
            switch (condition) {
                case IS_NULL:
                    conditon = " is null ";
                    break;
                case IS_NOT_NULL:
                    conditon = " is not null ";
                    break;
                case EQUAL:
                    conditon = "=";
                    break;
                case NOT_EQUAL:
                    conditon = "<>";
                    break;
                case GREATER_THAN:
                    conditon = ">";
                    break;
                case GREATER_THAN_OR_EQUAL:
                    conditon = ">=";
                    break;
                case LESS_THAN:
                    conditon = "<";
                    break;
                case LESS_THAN_OR_EQUAL:
                    conditon = "<=";
                    break;
                case LIKE:
                    conditon = "like";
                    break;
                case NOT_LIKE:
                    conditon = "not like";
                    break;
                case IN:
                    conditon = "in";
                    break;
                case NOT_IN:
                    conditon = "not in";
                    break;
                case BETWEEN:
                    conditon = "between";
                    break;
                case NOT_BETWEEN:
                    conditon = "not between";
                    break;
            }
            return column + " " + conditon;

        }
    }
}
